#include "displayablecontainer.h"

CDisplayableContainer::CDisplayableContainer()
    : m_pAnimator(CreateXAnimator(CDeviceManager::getInstance()->getDevice()))
{
    /**
     * Constructor, ensure that the animator object is initialised.
     */
}

CDisplayableContainer::~CDisplayableContainer()
{
    /**
     * Destructor, ensure that we clear up all the memory which was dynamically
     * created.
     */

    // Clear up all animations
    delete m_pAnimator;

    clearDisplayables();
}

void CDisplayableContainer::clearDisplayables()
{
    /**
     * Clears all displayable objects from the displayable containers.
     */
    if (! m_arrDisplayables.size())
    {
        // Some weird crash occurs if there are no elements in the array
        return;
    }
    for (vector<CDisplayable*>::iterator 
         pDisplayable(m_arrDisplayables.begin());
         pDisplayable != m_arrDisplayables.end(); ++pDisplayable)
    {
        delete (*pDisplayable);
    }
    m_arrDisplayables.clear();
}

CDisplayableContainer* CDisplayableContainer::getInstance()
{
    /**
     * @return CDisplayableContainer* A pointer to the static singleton.
     */
    static CDisplayableContainer s_instance;
    return &s_instance;
}

IXAnimator *CDisplayableContainer::getAnimatorObject() const
{
    /**
     * @return IXAnimator *A pointer to the animator object.
     */
    return m_pAnimator;
}

void CDisplayableContainer::insertDisplayable(CDisplayable *pDisplayable)
{
    /**
     * Inserts a displayable object into the array to store. This must have
     * been created on the heap.
     *
     * @param CDisplayable *pDisplayable The object to insert into the array
     */
    m_arrDisplayables.push_back(pDisplayable);
}

bool CDisplayableContainer::removeDisplayable(CDisplayable *pDisplayable)
{
    /**
     * Removes the displayable object from the array to be rendered.
     *
     * @param CDisplayable *pDisplayable The object to remove from the array
     */
    if (! m_arrDisplayables.size())
    {
        // For some reason, when the size of the vector is 0, begin and end
        // return const iterators, so just do a simple check here to avoid
        // crashes.
        return false;
    }

    for (vector<CDisplayable*>::iterator pPos(m_arrDisplayables.begin());
         pPos != m_arrDisplayables.end();)
    {
        if (*pPos == pDisplayable)
        {
            delete *pPos;
            pPos = m_arrDisplayables.erase(pPos);
            return true;
        }
        else
        {
            ++pPos;
        }
    }
    return false;
}

void CDisplayableContainer::render()
{
    /**
     * Loops through all the displayable objects and renders each one.
     */
    for (vector<CDisplayable*>::reverse_iterator 
         pDisplayable(m_arrDisplayables.rbegin());
         pDisplayable != m_arrDisplayables.rend(); ++pDisplayable)
    {
        if (*pDisplayable != nullptr)
        {
            if ((*pDisplayable)->canBeAutomaticallyRendered())
            {
                (*pDisplayable)->render();
            }
        }
    }
}

CDisplayable *CDisplayableContainer::createDisplayable()
{
    /**
     * Creates a blank displayable object with no mesh, then returns it.
     *
     * @return CDisplayable* A pointer to the newly created displayable object.
     */
    CDisplayable *pDisplayable = new CDisplayable;
    insertDisplayable(pDisplayable);
    return pDisplayable;
}

CDisplayable *CDisplayableContainer::createDisplayable(CMesh *pMesh)
{
    /**
     * Creates a displayable object, assigns the mesh to the displayable object
     * which is obtained from the parameter, then returns the dynamically
     * created object.
     *
     * @param CMesh *pMesh The mesh to assign to the displayable object.
     * @return CDisplayable* A pointer to the newly created displayable object.
     */
    CDisplayable *pDisplayable = new CDisplayable(pMesh);
    insertDisplayable(pDisplayable);
    return pDisplayable;
}

CDisplayable *CDisplayableContainer::createDisplayable(const string &szMeshKey)
{
    /**
     * Creates a displayable object, assigns the mesh to the displayable object
     * which is obtained from the mesh key, then returns the dynamically
     * created object.
     *
     * @param const string &szMeshKey The key of the mesh to assign.
     * @return CDisplayable* A pointer to the newly created displayable object.
     */
    CDisplayable *pDisplayable = new CDisplayable(szMeshKey);
    insertDisplayable(pDisplayable);
    return pDisplayable;
}